package com.zyc.datasettagger.controller;

import com.alibaba.druid.util.StringUtils;
import com.zyc.common.constants.SampleConstants;
import com.zyc.common.data.DataSetInfo;
import com.zyc.common.data.FileStoreInfo;
import com.zyc.common.data.SampleInfo;
import com.zyc.common.enums.ReturnCode;
import com.zyc.common.enums.SampleTypeEnum;
import com.zyc.common.enums.TagTypeEnum;
import com.zyc.common.exception.BizException;
import com.zyc.common.exception.EnumAcquireException;
import com.zyc.common.model.page.ListPage;
import com.zyc.common.model.page.SampleListPage;
import com.zyc.common.model.SampleModel;
import com.zyc.datasettagger.service.AuthService;
import com.zyc.datasettagger.service.DataSetService;
import com.zyc.datasettagger.service.SampleService;
import com.zyc.utils.ParamChecker;
import com.zyc.utils.convertor.SampleConvertor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zyc
 * @version 1.0
 */
@RestController
@CrossOrigin
@Slf4j
public class SampleController {
    static final String FILE_DISK_LOC = System.getProperty("os.name").equals("Mac OS X") ? SampleConstants.FILE_DISK_MAC : SampleConstants.FILE_DISK_ALI_ECS;
    SampleService sampleService;
    DataSetService dataSetService;
    TransactionTemplate transactionTemplate;

    AuthService authService;

    @Autowired
    public void setAuthService(AuthService authService) {
        this.authService = authService;
    }

    @Autowired
    public void setSampleService(SampleService sampleService) {
        this.sampleService = sampleService;
    }

    @Autowired
    public void setDataSetService(DataSetService dataSetService) {
        this.dataSetService = dataSetService;
    }

    @Autowired
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    @PostMapping("/api/sample")
    public SampleModel addSample(@RequestBody SampleModel sample) throws EnumAcquireException {
        ParamChecker.checkSampleModel(sample);

        // 1. 根据datasetId查询对应的tag和sample类型，并设置在sampleInfo中，之后直接调用方法set进去就好
        String datasetId = sample.getDataset_id();
        DataSetInfo dataSetInfo = dataSetService.getDataSetByDataSetId(datasetId);
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        if (!StringUtils.equals(dataSetInfo.getPublisherName(), username)) {
            log.warn("[addSample]-用户{}没有增加样本的权限", username);
            throw new BizException("没有权限", ReturnCode.RC403);
        }
        sample.setTag_type(dataSetInfo.getTagType().getName());
        sample.setSample_type(dataSetInfo.getSampleType().getName());
        SampleInfo sampleInfo = SampleConvertor.model2Info(sample);
        String sampleId = sampleService.addSample(sampleInfo);

        // 插入成功后返回自动生成的UUID，把这个UUID重新设置会SampleModel中
        sample.set_id(sampleId);

        return sample;
    }

    @GetMapping(value = "/api/samples", produces = MediaType.APPLICATION_JSON_VALUE)
    public SampleListPage getAllSample(@RequestParam("page_num") Integer pageNum,
                                       @RequestParam("page_size") Integer limitation,
                                       @RequestParam(value = "dataset_id", required = false) String dataset_id) {
        if (pageNum == null || limitation == null) {
            log.error("[getAllSample]-getAllSample请求参数有误，pageNum和limitation不能为null");
            throw new BizException("参数错误", ReturnCode.INVALID_INPUT);
        }
        if (pageNum <= 0) {
            log.error("[getAllSample]-传入的页数小于等于0");
            throw new BizException("传入的页码不能小于等于0", ReturnCode.INVALID_INPUT);
        }
        if (dataset_id == null) {
            // 没有指定dataset，全量展示？？
            return new SampleListPage(1, 1, new ArrayList<>(), limitation, 0);
        } else {
            DataSetInfo dataSetByDataSetId = dataSetService.getDataSetByDataSetId(dataset_id);
            String username = SecurityContextHolder.getContext().getAuthentication().getName();
            if (dataSetByDataSetId == null) {
                log.warn("[getAllSample]-未找到对应的dataset_id={}对应的dataset", dataset_id);
                return new SampleListPage(1, 1, new ArrayList<>(), limitation, 0);
            }

            if (!authService.hasDatasetTaggerOrOwnerRole(dataset_id)) {
                log.warn("[getAllSample]-用户{}没有查看样本数据集{}样本的权限", username, dataSetByDataSetId.getDatasetId());
                throw new BizException("没有权限", ReturnCode.RC403);
            }

            ListPage<SampleInfo> sampleEntityListPage = sampleService.getSampleByLimitation(dataset_id, pageNum, limitation);
            List<SampleInfo> sampleEntityList = sampleEntityListPage.getPageContent();
            List<SampleModel> sampleModelList = sampleEntityList.stream().map(SampleConvertor::data2Model).toList();
            return new SampleListPage(sampleEntityListPage.getCurPage(), sampleEntityListPage.getPageSize(), sampleModelList, sampleEntityListPage.getLimitation(), sampleEntityListPage.getTotal());
        }
    }

    @GetMapping(value = "/api/sample/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public SampleModel getSampleById(@PathVariable String id) {
        if (ObjectUtils.isEmpty(id)) {
            log.warn("[getSampleById]-传入参数id为空");
            throw new BizException("传入id不能为空", ReturnCode.INVALID_ID);
        }
        if (!authService.hasDatasetTaggerOrOwnerRole(id)) {
            log.warn("[getSampleById]-用户{}没有查看样本的{}权限", SecurityContextHolder.getContext().getAuthentication().getName(), id);
            throw new BizException("没有权限", ReturnCode.RC403);

        }
        SampleInfo sampleById = sampleService.getSampleById(id);
        return SampleConvertor.data2Model(sampleById);
    }

    @RequestMapping(value = "/api/sample/{id}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
    public String deleteSampleById(@PathVariable String id) {
        if (ObjectUtils.isEmpty(id)) {
            log.warn("[deleteSampleById]-传入参数id为空");
            throw new BizException("传入id不能为空", ReturnCode.INVALID_ID);
        }

        SampleInfo sampleById = sampleService.getSampleById(id);
        if (sampleById == null) {
            log.info("[deleteSampleById]-在删除过程中查找id={}的样本", id);
            throw new BizException("未找到对应样本", ReturnCode.DATA_NOT_FOUND);
        }

        DataSetInfo dataSetByDataSetId = dataSetService.getDataSetByDataSetId(sampleById.getDatasetId());
        if (dataSetByDataSetId == null) {
            log.info("[deleteSampleById]-在删除过程中查找id={}的数据集", id);
            throw new BizException("未找到样本对应的数据集", ReturnCode.DATA_NOT_FOUND);
        }

        // 校验权限
        String nameInContext = SecurityContextHolder.getContext().getAuthentication().getName();
        if (!ObjectUtils.nullSafeEquals(nameInContext, dataSetByDataSetId.getPublisherName())) {
            log.warn("[deleteSampleById]-没有删除样本集的权限");
            throw new BizException("没有删除样本的权限", ReturnCode.RC403);
        }

        FileStoreInfo fileStoreInfoBySampleId = sampleService.getFileStoreInfoBySampleId(id);
        int i = sampleService.deleteSampleById(id);
        if (fileStoreInfoBySampleId == null || fileStoreInfoBySampleId.getDist() == null) {
            return i == 1 ? "删除成功" : "没有删除到指定数据";
        }
        String dist = fileStoreInfoBySampleId.getDist();
        File file = new File(dist);
        if (file.exists()) {
            boolean delete = file.delete();
            if (delete) {
                log.info("[deleteSampleById]-删除sampleId = {}对应的磁盘文件", id);
            } else {
                log.warn("[deleteSampleById]-没有删除成功磁盘文件，需要手动检查, sampleId = {}, 对应的磁盘位置={}", id, dist);
            }
        }
        return "删除成功";

    }

    @PostMapping("/api/sample/upload/{datasetId}")
    public SampleModel uploadSample(@PathVariable String datasetId, @RequestBody MultipartFile file) {

        String name = file.getName();
        String contentType = file.getContentType();
        String originalFilename = file.getOriginalFilename();

        DataSetInfo dataSetByDataSetId = dataSetService.getDataSetByDataSetId(datasetId);
        if (dataSetByDataSetId == null) {
            log.error("[uploadSample]-datasetId = {}，没有对应的数据集", datasetId);
            throw new BizException("没有对应的数据集", ReturnCode.RC999);
        }

        // 权限校验
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        if (!StringUtils.equals(username, dataSetByDataSetId.getPublisherName())) {
            log.warn("[uploadSample]-该用户{}没有上传样本的权限", username);
            throw new BizException("没有操作权限", ReturnCode.RC403);
        }
        String sampleId = transactionTemplate.execute(new TransactionCallback<>() {
            @Override
            public String doInTransaction(TransactionStatus status) {
                String path = FILE_DISK_LOC + originalFilename;
                log.info("[uploadSample] - 生成保存文件的路径path = {}", path);
                String nginxUrl = SampleConstants.NGINX_RESOURCE_URL + originalFilename;
                String sampleId = insertSampleInfo(datasetId, path, nginxUrl);
                try {
                    file.transferTo(new File(path));
                    log.info("[uploadSample]-成功将样本保存在路径{}中", path);
                } catch (IOException e) {
                    throw new BizException("文件上传失败", ReturnCode.RC999);
                }
                return sampleId;
            }
        });
        long size = file.getSize();
        log.info("[uploadSample]-接收到参数name = {}, contentType= {}, originalFilename = {}, size = {}, datasetId={}", name, contentType, originalFilename, size, datasetId);
        SampleInfo sampleById = sampleService.getSampleById(sampleId);

        return SampleConvertor.data2Model(sampleById);
    }

    private String insertSampleInfo(String datasetId, String dist, String nginxUrl) {
        DataSetInfo dataSetByDataSetId = dataSetService.getDataSetByDataSetId(datasetId);
        SampleTypeEnum sampleType = dataSetByDataSetId.getSampleType();
        TagTypeEnum tagType = dataSetByDataSetId.getTagType();
        SampleInfo sampleInfo = new SampleInfo();
        sampleInfo.setSampleType(sampleType);
        sampleInfo.setTagType(tagType);
        sampleInfo.setDatasetId(datasetId);
        String sampleId = sampleService.addSample(sampleInfo);
        sampleService.addSampleDiskInfo(sampleId, dist, nginxUrl);
        return sampleId;
    }

    @GetMapping(value = "/api/sample/getResourceUrl/{sampleId}")
    public String getResourceUrl(@PathVariable String sampleId) {
        if (ObjectUtils.isEmpty(sampleId)) {
            log.error("[getResourceUrl]-sampleId为空");
            throw new BizException("sampleId不能为空", ReturnCode.INVALID_INPUT);
        }
        log.info("[getResourceUrl]-查找sampleId = {}的静态资源信息", sampleId);
        String nginxUrl = sampleService.getSampleResourceUrl(sampleId);
        return nginxUrl;
    }
}
